/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "render/lottie_render_node.h"
#include "bitmap.h"
#include "brush.h"
#include "canvas.h"
#include "color.h"
#include "common/common.h"
#include "common/number_constant.h"
#include "font.h"
#include "font_mgr.h"
#include "matrix.h"
#include "path.h"
#include "pen.h"
#include "point.h"
#include "rect.h"
#include "rlottiecommon.h"
#include "shader_effect.h"
#include "text_blob.h"
#include <bits/alltypes.h>
#include <native_drawing/drawing_canvas.h>
#include <native_drawing/drawing_color_filter.h>

namespace LottiePro {

LottieRenderNode::LottieRenderNode()
{
    brush_ = std::make_shared<drawing::Brush>();
    pen_ = std::make_shared<drawing::Pen>();
    path_ = std::make_shared<drawing::Path>();
}

LottieRenderNode::~LottieRenderNode()
{
}

void LottieRenderNode::ProcessNode(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node,
                                   std::shared_ptr<drawing::Path> rawClipMask)
{
    if (!node || !rawCanvas) {
        return;
    }
    // 检查节点是否需要渲染
    if (node->mFlag == ChangeFlagNone) {
        return;
    }

    // 处理文本节点
    if (node->mTextInfo.isTextNode && node->mTextInfo.text != nullptr) {
        ProcessTextNode(rawCanvas, node);
    }
    // 处理图像节点
    if (node->mImageInfo.data && node->mImageInfo.width > 0 && node->mImageInfo.height > 0) {
        ProcessImageNode(rawCanvas, node);
    } else {
        // 处理路径节点
        if (node->mPath.ptCount > 0) {
            ProcessPathNode(rawCanvas, node, rawClipMask);
        }
    }
}

void LottieRenderNode::ProcessPathNode(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node,
                                       std::shared_ptr<drawing::Path> rawClipMask)
{
    path_->Reset();
    ConvertLottiePathToDrawingPath(node->mPath.ptPtr, node->mPath.ptCount, node->mPath.elmPtr, node->mPath.elmCount,
                                   *path_);

    // 应用裁剪蒙版
    bool clipMask =
        rawClipMask &&
        (!strokeProps_ ||
         strokeProps_->showType != DISPLAY_BACKGROUND); // 控制stroke效果的时候，showType为1的时候，需要绘制canvas背景
    if (clipMask) {
        rawCanvas->SaveLayer(nullptr, nullptr);
        rawCanvas->ClipPath(rawClipMask->get(), INTERSECT, true);
    }

    if (node->mType == 0) {
        SetupBrush(rawCanvas, node);
    }

    if (node->mStroke.enable && node->mStroke.width > 0) {
        SetupPen(rawCanvas, node);
    } else if (node->mType != 0) {
        brush_->Reset();
        brush_->SetColor(0);
        rawCanvas->AttachBrush(*brush_);
    }

    // 绘制路径
    if (isRenderStroke()) {
        StrokeEffect(rawCanvas, rawClipMask);
    } else {
        rawCanvas->DrawPath(path_->get());
    }

    // 解除画笔和画刷绑定
    rawCanvas->DetachBrush();
    if (isDetachPen) {
        rawCanvas->DetachPen();
    }
    // 恢复状态
    if (clipMask) {
        rawCanvas->Restore();
    }
}

void LottieRenderNode::ProcessTextNode(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node)
{
    auto font = std::make_unique<drawing::Font>();
    if (!font) {
        DRAWING_LOGE("LottieRenderNode::ProcessTextNode: Failed to create font");
        return;
    }

    font->SetTextSize(node->mTextInfo.fontSize);

    // 尝试设置指定的字体,如果失败会自动使用默认字体
    bool fontLoaded = false;
    if (node->mTextInfo.fontName) {
        drawing::FontMgr fontMgr;
        auto styleSet = fontMgr.MatchFamily(node->mTextInfo.fontName);
        if (styleSet) {
            auto typeface = styleSet->CreateTypeface(0);
            if (typeface) {
                font->SetTypeface(typeface);
                fontLoaded = true;
            } else {
                DRAWING_LOGD("LottieRenderNode::ProcessTextNode: Failed to load font: %{public}s",
                             node->mTextInfo.fontName);
            }
        }
    }
    rawCanvas->Save();

    drawing::Matrix matrix;
    matrix.SetMatrix(node->mTextInfo.mTextMatrix.m11, node->mTextInfo.mTextMatrix.m21, node->mTextInfo.mTextMatrix.m31,
                     node->mTextInfo.mTextMatrix.m12, node->mTextInfo.mTextMatrix.m22, node->mTextInfo.mTextMatrix.m32,
                     node->mTextInfo.mTextMatrix.m13, node->mTextInfo.mTextMatrix.m23, node->mTextInfo.mTextMatrix.m33);
    rawCanvas->ConcatMatrix(matrix.get());

    uint32_t textColor =
        drawing::Color::SetArgb(node->mTextInfo.a, node->mTextInfo.r, node->mTextInfo.g, node->mTextInfo.b);
    brush_->Reset();
    brush_->SetColor(textColor);
    AttachBrush(rawCanvas);

    // 分割文本为多行，处理换行符\r
    std::string textStr(node->mTextInfo.text ? node->mTextInfo.text : " ");
    std::vector<std::string> lines;
    size_t start = 0;
    size_t end = 0;
    while ((end = textStr.find("\r", start)) != std::string::npos) {
        lines.push_back(textStr.substr(start, end - start));
        start = end + 1;
    }
    lines.push_back(textStr.substr(start));
    float lineHeight = node->mTextInfo.lineHeight;
    int textJustify = static_cast<int>(node->mTextInfo.textJustify);
    float y = 0;
    constexpr float half = 2.0f;
    for (const auto &line : lines) {
        if (line.empty()) {
            y += lineHeight;
            continue;
        }
        // 计算文本宽度
        float textWidth = 0;
        font->MeasureText(line.c_str(), line.length(), OH_Drawing_TextEncoding::TEXT_ENCODING_UTF8, nullptr,
                          &textWidth);
        float x = 0;
        switch (textJustify) {
            case NUM_1 :
                x = -textWidth;
                break;
            case NUM_2 :
                x = -textWidth / half;
                break;
            case NUM_0 :
            default :
                x = 0;
                break;
        }
        // 创建textBlob,绘制文本
        auto textBlob =
            drawing::TextBlob::CreateFromString(line.c_str(), font->get(), OH_Drawing_TextEncoding::TEXT_ENCODING_UTF8);

        // 处理描边
        if (node->mTextInfo.hasStroke && (node->mTextInfo.sr > 0 || node->mTextInfo.sg > 0 || node->mTextInfo.sb > 0) &&
            node->mTextInfo.sa > 0) {
            pen_->Reset();
            uint32_t strokeColor =
                drawing::Color::SetArgb(node->mTextInfo.sa, node->mTextInfo.sr, node->mTextInfo.sg, node->mTextInfo.sb);
            pen_->SetColor(strokeColor);

            float strokeWidth = node->mTextInfo.strokeWidth;
            pen_->SetWidth(strokeWidth);
            rawCanvas->AttachPen(*pen_);
            rawCanvas->DrawTextBlob(textBlob->get(), x, y);
            rawCanvas->DetachPen();
        } else {
            DRAWING_LOGD("LottieRenderNode::ProcessTextNode: Failed to create text blob");
        }
        rawCanvas->DrawTextBlob(textBlob->get(), x, y);
        y += lineHeight;
    }
    rawCanvas->DetachBrush();
    rawCanvas->Restore();
}

void LottieRenderNode::ProcessImageNode(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node)
{
    drawing::Bitmap bitmap;
    OH_Drawing_Image_Info imageInfo = {static_cast<int32_t>(node->mImageInfo.width),
                                       static_cast<int32_t>(node->mImageInfo.height), COLOR_FORMAT_BGRA_8888,
                                       ALPHA_FORMAT_PREMUL};
    bitmap = bitmap.CreateFromPixels(&imageInfo, node->mImageInfo.data, node->mImageInfo.width * NUM_4);
    rawCanvas->Save();
    drawing::Matrix matrix;
    matrix.SetMatrix(node->mImageInfo.mMatrix.m11, node->mImageInfo.mMatrix.m21, node->mImageInfo.mMatrix.m31,
                     node->mImageInfo.mMatrix.m12, node->mImageInfo.mMatrix.m22, node->mImageInfo.mMatrix.m32,
                     node->mImageInfo.mMatrix.m13, node->mImageInfo.mMatrix.m23, node->mImageInfo.mMatrix.m33);
    rawCanvas->ConcatMatrix(matrix.get());
    rawCanvas->DrawBitmap(bitmap.get(), 0.0f, 0.0f);
    rawCanvas->Restore();
}

void LottieRenderNode::SetupBrush(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node)
{
    if (!node || !rawCanvas) {
        return;
    }
    brush_->Reset();
    switch (node->mBrushType) {
        case BrushSolid : {
            // 纯色填充
            uint32_t color = drawing::Color::SetArgb(node->mColor.a, node->mColor.r, node->mColor.g, node->mColor.b);
            brush_->SetColor(color);
            break;
        }
        case BrushGradient : {
            // 渐变填充
            if (!node->mGradient.stopPtr || node->mGradient.stopCount <= 0) {
                break;
            }
            // 准备渐变色和位置数组
            std::vector<uint32_t> colors(node->mGradient.stopCount);
            std::vector<float> positions(node->mGradient.stopCount);
            for (size_t i = 0; i < node->mGradient.stopCount; i++) {
                colors[i] = drawing::Color::SetArgb(node->mGradient.stopPtr[i].a, node->mGradient.stopPtr[i].r,
                                                    node->mGradient.stopPtr[i].g, node->mGradient.stopPtr[i].b);
                positions[i] = node->mGradient.stopPtr[i].pos;
            }
            if (node->mGradient.type == GradientLinear) {
                OH_Drawing_Point2D startPt = {node->mGradient.start.x, node->mGradient.start.y};
                OH_Drawing_Point2D endPt = {node->mGradient.end.x, node->mGradient.end.y};
                brush_->SetLinearShaderEffect(&startPt, &endPt, colors.data(), positions.data(),
                                              node->mGradient.stopCount, CLAMP, nullptr);
            } else if (node->mGradient.type == GradientRadial) {
                OH_Drawing_Point2D centerPt = {node->mGradient.center.x, node->mGradient.center.y};
                OH_Drawing_Point2D endPt = {node->mGradient.focal.x, node->mGradient.focal.y};
                brush_->SetRadialShaderEffect(&centerPt, node->mGradient.cradius, &endPt, node->mGradient.fradius,
                                              colors.data(), positions.data(), node->mGradient.stopCount, CLAMP,
                                              nullptr);
            }
            break;
        }
        default :
            DRAWING_LOGE("LottieRenderNode::SetupBrush: Unknown brush type");
            break;
    }
    brush_->SetAntiAlias(true);
    AttachBrush(rawCanvas);
}

void LottieRenderNode::SetupPen(std::shared_ptr<drawing::Canvas> rawCanvas, const LOTNode *node)
{
    if (!node || !rawCanvas || !node->mStroke.enable) {
        return;
    }

    pen_->Reset();
    // 设置线条颜色
    switch (node->mBrushType) {
        case BrushSolid : {
            // 纯色填充
            uint32_t color = drawing::Color::SetArgb(node->mColor.a, node->mColor.r, node->mColor.g, node->mColor.b);
            pen_->SetColor(color);
            break;
        }
        case BrushGradient : {
            // 渐变填充
            if (!node->mGradient.stopPtr || node->mGradient.stopCount <= 0) {
                break;
            }
            // 准备渐变色和位置数组
            std::vector<uint32_t> colors(node->mGradient.stopCount);
            std::vector<float> positions(node->mGradient.stopCount);
            for (size_t i = 0; i < node->mGradient.stopCount; i++) {
                colors[i] = drawing::Color::SetArgb(node->mGradient.stopPtr[i].a, node->mGradient.stopPtr[i].r,
                                                    node->mGradient.stopPtr[i].g, node->mGradient.stopPtr[i].b);
                positions[i] = node->mGradient.stopPtr[i].pos;
            }
            if (node->mGradient.type == GradientLinear) {
                OH_Drawing_Point2D startPt = {node->mGradient.start.x, node->mGradient.start.y};
                OH_Drawing_Point2D endPt = {node->mGradient.end.x, node->mGradient.end.y};
                pen_->SetLinearShaderEffect(&startPt, &endPt, colors.data(), positions.data(),
                                            node->mGradient.stopCount, CLAMP, nullptr);
            } else if (node->mGradient.type == GradientRadial) {
                OH_Drawing_Point2D centerPt = {node->mGradient.center.x, node->mGradient.center.y};
                OH_Drawing_Point2D endPt = {node->mGradient.focal.x, node->mGradient.focal.y};
                pen_->SetRadialShaderEffect(&centerPt, node->mGradient.cradius, &endPt, node->mGradient.fradius,
                                            colors.data(), positions.data(), node->mGradient.stopCount, CLAMP, nullptr);
            }
            break;
        }
        default :
            DRAWING_LOGE("LottieRenderNode::SetupPen: Unknown brush type");
            break;
    }

    // 设置线条宽度
    pen_->SetWidth(node->mStroke.width);

    // 设置线条端点样式
    drawing::Pen::LineCapStyle capStyle;
    switch (node->mStroke.cap) {
        case CapSquare :
            capStyle = LINE_SQUARE_CAP;
            break;
        case CapRound :
            capStyle = LINE_ROUND_CAP;
            break;
        case CapFlat :
        default :
            capStyle = LINE_FLAT_CAP;
            break;
    }
    pen_->SetLineCap(capStyle);

    // 设置线条连接样式
    drawing::Pen::LineJoinStyle joinStyle;
    switch (node->mStroke.join) {
        case JoinBevel :
            joinStyle = LINE_BEVEL_JOIN;
            break;
        case JoinRound :
            joinStyle = LINE_ROUND_JOIN;
            break;
        case JoinMiter :
        default :
            joinStyle = LINE_MITER_JOIN;
            break;
    }
    pen_->SetLineJoin(joinStyle);

    // 设置斜接限制
    pen_->SetMiterLimit(node->mStroke.miterLimit);
    pen_->SetAntiAlias(true);
    // 附加画笔到画布
    rawCanvas->AttachPen(*pen_);
}

void LottieRenderNode::ConvertLottiePathToDrawingPath(const float *ptPtr, size_t ptCount, const char *elmPtr,
                                                      size_t elmCount, drawing::Path &path)
{
    if (!ptPtr || !elmPtr || ptCount < NUM_2 || elmCount < NUM_1) {
        return;
    }

    size_t ptIndex = 0;
    for (size_t i = 0; i < elmCount; i++) {
        switch (elmPtr[i]) {
            case NUM_0 : // MoveTo
                if (ptIndex + 1 < ptCount) {
                    path.MoveTo(ptPtr[ptIndex], ptPtr[ptIndex + 1]);
                    ptIndex += NUM_2;
                }
                break;
            case NUM_1 : // LineTo
                if (ptIndex + 1 < ptCount) {
                    path.LineTo(ptPtr[ptIndex], ptPtr[ptIndex + 1]);
                    ptIndex += NUM_2;
                }
                break;
            case NUM_2 : // CubicTo
                if (ptIndex + NUM_5 < ptCount) {
                    path.CubicTo(ptPtr[ptIndex], ptPtr[ptIndex + 1], ptPtr[ptIndex + NUM_2], ptPtr[ptIndex + NUM_3],
                                 ptPtr[ptIndex + NUM_4], ptPtr[ptIndex + NUM_5]);
                    ptIndex += NUM_6;
                }
                break;
            case NUM_4 : // QuadTo
                if (ptIndex + NUM_3 < ptCount) {
                    path.QuadTo(ptPtr[ptIndex], ptPtr[ptIndex + 1], ptPtr[ptIndex + NUM_2], ptPtr[ptIndex + NUM_3]);
                    ptIndex += NUM_4;
                }
                break;
            case NUM_3 : // Close
                path.Close();
                break;
            default :
                DRAWING_LOGE("LottieRenderNode::ConvertLottiePathToDrawingPath: Unknown path element: %{public}c",
                             elmPtr[i]);
                break;
        }
    }
}

std::shared_ptr<drawing::DrawFilter> LottieRenderNode::SetBluer(std::shared_ptr<drawing::Canvas> rawCanvas, float rx,
                                                                float ry)
{
    auto draw_filter = std::make_shared<drawing::DrawFilter>();
    draw_filter->SetFilter(rx, ry);
    pen_->SetFilter(draw_filter->getFilter());
    rawCanvas->AttachPen(*pen_);
    this->isDetachPen = false;
    return draw_filter;
}

void LottieRenderNode::AttachBrush(std::shared_ptr<drawing::Canvas> rawCanvas)
{
    constexpr size_t colorLimits = 255;
    if (colorPercent_) {
        uint32_t color =
            drawing::Color::SetArgb(colorPercent_.get()->opacity * colorLimits, colorPercent_.get()->r * colorLimits,
                                    colorPercent_.get()->g * colorLimits, colorPercent_.get()->b * colorLimits);
        brush_->SetColor(color);
    }
    if (colorBlack.size() > 0 && colorWhite.size() > 0) {
        TintEffect(rawCanvas, colorBlack, colorWhite, intensity);
    }
    if (triToneColor.size() >= NUM_3) {
        TriToneEffect(rawCanvas);
    }
    rawCanvas->AttachBrush(*brush_);
}

void LottieRenderNode::TintEffect(std::shared_ptr<drawing::Canvas> rawCanvas, std::vector<float> colorBlack,
                                  std::vector<float> colorWhite, float intensity)
{
    if (colorBlack.size() > 0 && colorWhite.size() > 0) {
        const float matrix[20] = {(colorWhite[0] - colorBlack[0]),
                                  0,
                                  0,
                                  0,
                                  colorBlack[0],
                                  (colorWhite[1] - colorBlack[1]),
                                  0,
                                  0,
                                  0,
                                  colorBlack[1],
                                  (colorWhite[NUM_2] - colorBlack[NUM_2]),
                                  0,
                                  0,
                                  0,
                                  colorBlack[NUM_2],
                                  0,
                                  0,
                                  0,
                                  1,
                                  0};
        constexpr size_t MaxLength = 20;
        float blendedMatrix[MaxLength];
        // 单位矩阵
        const float identity[MaxLength] = {0.3333, 0, 0, 0, 0, 0, 0.3333, 0, 0, 0, 0, 0, 0.3333, 0, 0, 0, 0, 0, 1, 0};
        for (int i = 0; i < MaxLength; i++) {
            if (i % NUM_5 == NUM_4) { // 偏移量直接线性插值
                blendedMatrix[i] = intensity * matrix[i];
            } else { // 乘数部分混合
                blendedMatrix[i] = (1 - intensity) * identity[i] + intensity * matrix[i];
            }
        }
        auto draw_filter = std::make_shared<drawing::DrawFilter>();
        draw_filter->SetFilterSrgbMatrix(blendedMatrix);
        brush_->SetEffectFilter(draw_filter->getFilter());
        rawCanvas->AttachBrush(*brush_);
    }
}

void LottieRenderNode::TriToneEffect(std::shared_ptr<drawing::Canvas> rawCanvas)
{
    constexpr size_t colorsOffset = 8;
    constexpr size_t colorsOffset1 = 16;
    constexpr size_t colorsOffset2 = 24;
    constexpr size_t colorMask = 0xff;
    constexpr size_t shadow = 85;
    constexpr size_t midtones = 170;
    constexpr float mask = 255.0;

    auto colors = brush_->GetColor();
    auto r = (colors >> colorsOffset1) & colorMask;
    auto g = (colors >> colorsOffset) & colorMask;
    auto b = (colors)&colorMask;
    // 计算灰度值（加权平均）
    uint8_t luminance = static_cast<uint8_t>(0.299 * r + 0.587 * g + 0.114 * b);
    auto shadowColor = triToneColor[NUM_0];
    auto midToneColor = triToneColor[NUM_1];
    auto highlightColor = triToneColor[NUM_2];
    if (luminance < shadow) { // 阴影
        r = shadowColor[NUM_0] * mask;
        g = shadowColor[NUM_1] * mask;
        b = shadowColor[NUM_2] * mask;
    } else if (luminance < midtones) { // 中间调
        r = midToneColor[NUM_0] * mask;
        g = midToneColor[NUM_1] * mask;
        b = midToneColor[NUM_2] * mask;
    } else { // 高光
        r = highlightColor[NUM_0] * mask;
        g = highlightColor[NUM_1] * mask;
        b = highlightColor[NUM_2] * mask;
    }
    uint32_t color = drawing::Color::SetArgb((colors >> colorsOffset2) & colorMask, static_cast<uint32_t>(r),
                                             static_cast<uint32_t>(g), static_cast<uint32_t>(b));
    brush_->SetColor(color);
    rawCanvas->AttachBrush(*brush_);
}

void LottieRenderNode::StrokeEffect(std::shared_ptr<drawing::Canvas> rawCanvas,
                                    std::shared_ptr<drawing::Path> rawClipMask)
{
    if (!rawClipMask || !strokeProps_) {
        rawCanvas->DrawPath(path_->get());
        return;
    }
    constexpr float mask = 255.0;
    rawCanvas->DetachBrush();
    pen_->SetAntiAlias(true);
    if (strokeProps_->showType == DISPLAY_OPACITY || strokeProps_->showType == DISPLAY_BACKGROUND) {
        uint32_t color =
            drawing::Color::SetArgb(strokeProps_.get()->colors[NUM_3] * mask, strokeProps_.get()->colors[NUM_0] * mask,
                                    strokeProps_.get()->colors[NUM_1] * mask, strokeProps_.get()->colors[NUM_2] * mask);
        pen_->SetColor(color);
        if (strokeProps_->showType == DISPLAY_BACKGROUND) {
            rawCanvas->DrawColor(brush_->GetColor(), OH_Drawing_BlendMode::BLEND_MODE_HARD_LIGHT);
        }
    } else {
        pen_->SetColor(brush_->GetColor());
    }
    pen_->SetWidth(strokeProps_.get()->width);
    pen_->SetAlpha(strokeProps_.get()->opacity * mask);
    if (strokeProps_.get()->lineSpace > 0) {
        float space = strokeProps_->width * NUM_2 * strokeProps_.get()->lineSpace * 0.01;
        float intervals[] = {1, space};
        pen_->SetDashPathEffect(intervals, sizeof(intervals) / sizeof(float), 0.0);
    }
    rawCanvas->AttachPen(*pen_);
    rawCanvas->DrawPath(rawClipMask->get());
}

bool LottieRenderNode::isRenderStroke()
{
    return strokeProps_ && strokeProps_->isAllMask &&
           (strokeProps_->showType == DISPLAY_BACKGROUND || strokeProps_->showType == DISPLAY_OPACITY ||
            strokeProps_->showType == DISPLAY_LINE);
}

void LottieRenderNode::ClearEffectOfEf(){
    //清除ef的fill效果
    if (colorPercent_) {
        colorPercent_ = nullptr;
    }
    //清除ef的Tint效果
    if (colorBlack.size() > 0) {
       std::vector<float>().swap(colorBlack);
    }
    if (colorWhite.size() > 0) {
        std::vector<float>().swap(colorWhite);
    }
    //清除ef的triTone效果
    if (triToneColor.size() > 0) {
        std::vector<std::vector<float>>().swap(triToneColor);
    }
    //清除ef的stroke效果
    if (strokeProps_) {
        strokeProps_ = nullptr;
    }
}
} // namespace LottiePro
